#ifndef ___fat32_recursos_y_bloqueos_sobre_archivos_y_directorios

#define ___fat32_recursos_y_bloqueos_sobre_archivos_y_directorios 1

#include "f32_bloq.c"
#include "f32_rec.c"
#include "f32_rb_1.c"
#include "f32_rb_2.c"
#include "f32_aux.c"
#include "f32_rb_3.c"
#include "f32_base.c"
#include "f32_rb_4.c"

enum fat32_niveles_bloqueo {
  FAT32_BLOQUEO_ARCHIVO_LECTURA = 1,
  FAT32_BLOQUEO_ARCHIVO_ESCRITURA = 2,
  FAT32_BLOQUEO_ARCHIVO_TOTAL = 3,
  FAT32_BLOQUEO_DIRECTORIO_LECTURA_ENTRADA = 4,
  FAT32_BLOQUEO_DIRECTORIO_LECTURA_GENERAL = 5,
  FAT32_BLOQUEO_DIRECTORIO_ESCRITURA_ENTRADA = 6,
  FAT32_BLOQUEO_DIRECTORIO_TOTAL = 7
}

unsigned long long fat32_obtener_acceso_sobre_elemento_por_nombre(unsigned int, char **, unsigned char);
int fat32_obtener_acceso_sobre_elemento(unsigned int, unsigned long long, unsigned char);
void fat32_liberar_acceso_sobre_elemento(unsigned int, unsigned long long);
unsigned long long fat32_generar_descriptor(char **, unsigned char);
int fat32_liberar_descriptor(unsigned long long);
int fat32_abrir_elemento(unsigned int, fat32_apertura_archivo);
void fat32_cerrar_elemento(unsigned long long);
fat32_apertura_archivo fat32_obtener_apertura(unsigned long long);
void fat32_modificar_apertura(fat32_apertura_archivo);
int fat32_apertura_existe(unsigned long long);

unsigned long long fat32_obtener_acceso_sobre_elemento_por_nombre (unsigned int transaccion, char ** ruta, unsigned char nivel) {
  unsigned long long descriptor = fat32_generar_descriptor(ruta, nivel >= 4);
  int resultado = fat32_obtener_acceso_sobre_elemento(transaccion, descriptor, nivel);
  if (resultado) return descriptor; else return 0;
}

int fat32_obtener_acceso_sobre_elemento (unsigned int transaccion, unsigned long long numDescriptor, unsigned char nivel) {
  if ((nivel < 1) || (nivel > 7)) return -1;
  fat32_descriptor_archivo * descriptor = fat32_buscar_descriptor(numDescriptor);
  if (!descriptor) return -2;
  if (((descriptor -> tipo) && (nivel < 4)) || (!(descriptor -> tipo) && (nivel >= 4))) return -3;
  if (fat32_bloqueo_transaccion_sobre_elemento(transaccion, descriptor)) return -4;
  unsigned char bloqueoActual = fat32_obtener_bloqueo_sobre_elemento(numDescriptor);
  if (bloqueoActual && !(
       ((bloqueoActual == 1) && (nivel == 1)) || (((bloqueoActual == 4) || (bloqueoActual == 5)) && ((nivel == 4) || (nivel == 5)))
     ))
    return fat32_generar_nuevo_bloqueo_para_elemento(transaccion, numDescriptor, nivel);
  else {
    fat32_generar_nueva_apertura_para_elemento(transaccion, numDescriptor, nivel);
    return 0;
  }
}

void fat32_liberar_acceso_sobre_elemento (unsigned int transaccion, unsigned long long descriptor) {
  if (!fat32_bloqueo_transaccion_sobre_elemento(transaccion, descriptor)) return;
  int posicion;
  for (posicion = 0; posicion < fat32_recursos.cantArchivos; posicion ++)
    if ((fat32_recursos.archivos[posicion].transaccion = transaccion) && (fat32_recursos.archivos[posicion].descriptor == descriptor)) {
      fat32_recursos.archivos[posicion] = fat32_recursos.archivos[-- fat32_recursos.cantArchivos];
      fat32_recursos.archivos = fb_cambiar_tamano(fat32_recursos.archivos, sizeof(fat32_acceso_archivo), fat32_recursos.cantArchivos);
    }
}

unsigned long long fat32_generar_descriptor (char ** ruta, unsigned char tipo) {
  unsigned long long descriptor = fat32_descriptor_existe(ruta);
  if (descriptor) {
    unsigned long pos;
    for (pos = 0; pos < fat32_recursos.cantDescriptores; pos ++)
      if (fat32_recursos.descriptores[pos].descriptor == descriptor)
        fat32_recursos.descriptores[pos].dependientes ++;
    return descriptor;
  }
  return fat32_nuevo_descriptor(ruta, tipo);
}

int fat32_liberar_descriptor (unsigned long long descriptor) {
  if (fat32_obtener_bloqueo_sobre_elemento(descriptor)) return 1;
  unsigned int posicion;
  for (posicion = 0; posicion < fat32_recursos.cantDescriptores; posicion ++)
    if (((fat32_recursos.descriptores)[posicion].descriptor) == descriptor) {
      fat32_recursos.descriptores[posicion].dependientes --;
      if (!fat32_recursos.descriptores[posicion].dependientes) {
        fat32_destruir_ruta(fat32_recursos.descriptores[posicion].ruta);
        fat32_recursos.cantDescriptores --;
        (fat32_recursos.descriptores)[posicion] = (fat32_recursos.descriptores)[fat32_recursos.cantDescriptores];
        fat32_recursos.descriptores = fb_cambiar_tamano(fat32_recursos.descriptores, sizeof(fat32_descriptor_archivo),
                                                        fat32_recursos.cantDescriptores);
      }
      return 0;
    }
  return 2;
}

int fat32_abrir_elemento (unsigned int transaccion, fat32_apertura_archivo datosApertura) {
  fat32_descriptor_archivo * descriptor = fat32_buscar_descriptor(datosApertura.descriptor);
  if (!descriptor) return -2;
  if (descriptor -> tipo) return -1;
  if (fat32_buscar_apertura_elemento(datosApertura.descriptor) != -1) return -3;
  unsigned char bloqueoActual = fat32_obtener_bloqueo_sobre_elemento(datosApertura.descriptor);
  if (bloqueoActual) {
    fat32_generar_nuevo_bloqueo_apertura(transaccion, datosApertura);
    return 1;
  } else {
    fat32_generar_nueva_apertura_para_elemento(0, datosApertura.descriptor,
                                               datosApertura.tipo ? FAT32_BLOQUEO_ARCHIVO_ESCRITURA : FAT32_BLOQUEO_ARCHIVO_LECTURA);
    fat32_crear_apertura_elemento(datosApertura);
    return 0;
  }
}

void fat32_cerrar_elemento (unsigned long long descriptor) {
  int posicion = fat32_buscar_apertura_elemento(descriptor);
  if (posicion == -1) return;
  fat32_liberar_acceso_sobre_elemento(0, descriptor);
  fat32_recursos.cantAperturas --;
  fat32_recursos.aperturas[posicion] = fat32_recursos.aperturas[fat32_recursos.cantAperturas];
  fat32_recursos.aperturas = fb_cambiar_tamano(fat32_recursos.aperturas, sizeof(fat32_apertura_archivo), fat32_recursos.cantAperturas);
}

fat32_apertura_archivo fat32_obtener_apertura (unsigned long long descriptor) {
  int posicion = fat32_buscar_apertura_elemento(descriptor);
  if (posicion != -1) return fat32_recursos.aperturas[posicion];
  fat32_apertura_archivo x;
  x.descriptor = 0;
  x.tipo = 255;
  return x;
}

void fat32_modificar_apertura (fat32_apertura_archivo datosApertura) {
  int posicion = fat32_buscar_apertura_elemento(datosApertura.descriptor);
  if (posicion == -1) return.
  datosApertura.tipo = fat32_aperturas[posicion].tipo;
  fat32_aperturas[posicion] = datosApertura;
}

int fat32_apertura_existe (unsigned long long descriptor) {
  return fat32_buscar_apertura_elemento(descriptor) != -1;
}

#endif